package com.woniu.service;

import com.alibaba.fastjson.JSON;
import com.woniu.entity.Question;
import com.woniu.entity.QuestionFeedback;
import com.woniu.entity.dto.ExamContent;
import com.woniu.entity.dto.ExamResult;
import com.woniu.entity.dto.ExamSubmission;
import com.woniu.utils.ConversationContextManager;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.converter.BeanOutputConverter;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;

import java.util.ArrayList;
import java.util.List;

/**
 * @author 饶志
 * @version 1.0
 * @since 2025-06-11  14:15
 */
@Service
public class ChatService {

    private final ChatClient chatClient;

    @Autowired
    private ConversationContextManager contextManager;


    public ChatService(ChatClient.Builder chatClientBuilder, ToolCallbackProvider tools) {
        this.chatClient = chatClientBuilder
                .defaultToolCallbacks(tools)
                .defaultSystem("""
                        你是蜗牛AI在线课堂的智能助手，为用户提供全面且专业的课程咨询与学习支持服务。
                        蜗牛AI在线课堂是蜗牛旗下专注于IT全方位教学的在线课堂品牌，课程价格在9.9 - 999元之间，强调性价比。
                        涵盖课程体系、视频、笔记、源码以及在线编辑器等丰富资源。
                        你需要精准理解用户的问题，结合蜗牛AI在线课堂的课程特点、功能模块和服务内容进行回答。
                        无论是课程详情、学习进度管理、积分规则，还是AI智能体相关功能，都要给予准确且清晰的解答。
                        当用户有学习需求时，提供合理的课程推荐和学习建议；遇到技术问题，尽力协助解决或指引解决方向。
                        作为蜗牛AI在线课堂的AI智能助手，你的核心任务是引导学员高效学习。
                        
                        1、用户与AI是有上下文记忆的。
                        2、课程分类情况是：分为单课程、实践课程、体系课程、就业课程四个大类，每个类别下有多个子类别，每个子类别下有多个阶段分类，阶段分类下面才是课程信息，其他三个大类及其子类别下都不能有课程联系信息，
                        而单课程与其他三个大类不同，单课程下面直接是课程信息。
                        所以添加课程或添加分类时请参考此情况信息，确保添加的课程或分类关联信息正确。
                        3、在添加操作时，你需要收集工具的全部所需参数，并询问用户确认信息是否正确，在下一轮会话时用户回复确认后你才能进行添加操作。
                        4、某些方法有权限限制，方法里会进行权限判断，我会给你提供当前用户的sessionUserId，你无需向用户询问ID信息。
                        5、若用户给的需求信息不明确，你需要引导用户补充信息，直到用户给出明确的需求信息。
                        6、若调用工具出现报错，请向用户返回“抱歉，当前内部出现了点问题，请稍后再试~”。
                        7、凡是mcp工具方法描述写了“只有用户回复确认后才能使用”，就必须询问用户的确认意向，在下一轮会话中用户回复确认后才能调用该工具方法，否则禁止调用！
                        
                        注意：对于MCP天气和新闻工具的使用，只有用户明确要求时再使用，否则不要使用相关MCP工具。
                             如果历史对话中已经查询过天气，则不再重复查询，直接使用历史查询结果。
                             当用户提出添加课程时，需要先查询分类叶子节点分类id，再查询教师id，然后才能够进行添加课程操作，否则没有完整信息时不能进行添加课程操作, 不能擅自连续调用添加工具方法!
                        """)
//                .defaultTools()
                .build();
    }

    /**
     * 处理用户的普通（非流式）聊天查询请求
     *
     * @param sessionId 用户会话ID（用于标识不同用户的对话上下文）
     * @param userInput 用户输入的聊天内容
     * @return AI生成的文本响应
     */
    public String processQuery(String sessionId, String userInput) {
        // 将用户当前输入添加到对应会话的上下文记录中
        contextManager.addUserInput(sessionId, userInput);
        // 获取该会话的完整对话上下文（包含历史对话记录）
        List<String> context = contextManager.getContext(sessionId);

        // 将上下文列表拼接为连续文本（用换行符分隔），作为LLM的输入
        String fullInput = String.join("\n", context);
        // ❗调用聊天客户端发送完整输入并获取同步响应
        String response = chatClient.prompt(fullInput).call().content();

        // 将AI生成的响应添加到对应会话的上下文记录中
        if (response != null) {
            contextManager.addAIResponse(sessionId, response);
        }
        return response;
    }

    /**
     * 处理用户的流式聊天查询请求（逐块返回响应）
     * 已处理会话上下文记忆功能
     *
     * @param sessionId 用户会话ID（用于标识不同用户的对话上下文）
     * @param userInput 用户输入的聊天内容
     * @return 流式响应的Flux（可逐块订阅响应内容）
     */
    public Flux<String> processQueryStream(String sessionId, String userInput) {

        BeanOutputConverter<String> parser = new BeanOutputConverter<>(String.class);

        // 将用户当前输入添加到对应会话的上下文记录中
        contextManager.addUserInput(sessionId, userInput);
        // 获取该会话的完整对话上下文（包含历史对话记录）
        List<String> context = contextManager.getContext(sessionId);

        // 将上下文列表拼接为连续文本（用换行符分隔），作为LLM的输入
        String fullInput = String.join("\n", context) + "。sessionUserId:" + sessionId;
        // ❗调用聊天客户端发送完整输入并获取流式响应（逐块返回）
        Flux<String> responseStream = chatClient.prompt(fullInput)
                .user(u -> u.text(userInput)
                        .param("format", parser.getFormat())
                        .param("structure", "使用自然段落格式，每个段落之间用两个换行符分隔"))
                .stream()
                .content()
                .map(content -> content.replace("\n\n", "<br><br>")
                        .replace("\n", "<br/>"));

        // 使用StringBuilder收集流式响应的各个片段
        StringBuilder aiResponseBuilder = new StringBuilder();
        // 对流式响应进行处理：
        return responseStream
                // 每个响应片段到达时，拼接到StringBuilder中
                .doOnNext(aiResponseBuilder::append)
                // 当流式响应完成时，将完整拼接的响应保存到对话上下文
                .doOnComplete(() -> {
                    String aiResponse = aiResponseBuilder.toString();
                    contextManager.addAIResponse(sessionId, aiResponse);
                });
    }

    // 生成试卷内容
    public ExamContent generateExamContent(String content, int count) {
        String promptText = String.format("""
            根据以下视频内容生成%d道选择题，并以严格的JSON格式返回：
            {
                "examTitle": "视频内容测试",
                "questions": [
                    {
                        "type": "choice",
                        "content": "问题内容",
                        "options": ["选项A", "选项B", "选项C", "选项D"],
                        "answer": "A",
                        "explanation": "解析内容",
                        "score": 10
                    }
                ]
            }
            要求：
            1. 所有题目必须基于提供的视频内容生成
            2. 每题必须有4个选项，标记为A-D
            3. 正确答案只能为A、B、C或D
            4. 每题分值为10分
            5. 不要包含markdown或代码块标记
            6. 输出必须是纯JSON格式
            
            视频内容：
            %s
            """, count, content);

//        logger.info("Prompt sent to AI: {}", promptText);

        ExamContent entity = chatClient.prompt()
                .user(u -> u.text(promptText))
                .call()
                .entity(ExamContent.class);
        String json = JSON.toJSONString(entity);
        System.out.println(json);
        return entity;

    }

    public ExamResult gradeExam(ExamSubmission examSubmission) {
        ExamResult result = new ExamResult();
        List<QuestionFeedback> feedbacks = new ArrayList<>();

        int score = 0;
        for (int i = 0; i < examSubmission.getQuestions().size(); i++) {
            Question question = examSubmission.getQuestions().get(i);
            String userAnswer = i < examSubmission.getAnswers().size() ?
                    examSubmission.getAnswers().get(i) : "未回答";

            QuestionFeedback feedback = new QuestionFeedback();
            boolean isCorrect = userAnswer.trim().equalsIgnoreCase(question.getAnswer().trim());
            feedback.setCorrect(isCorrect);

            if (isCorrect) {
                score += question.getScore();
                feedback.setComment("回答正确！" + (question.getExplanation() != null ?
                        "解析：" + question.getExplanation() : ""));
            } else {
                feedback.setComment("回答错误。正确答案是：" + question.getAnswer() +
                        (question.getExplanation() != null ? "。解析：" + question.getExplanation() : ""));
            }

            feedbacks.add(feedback);
        }

        result.setScore(score);
        result.setFeedbacks(feedbacks);
        return result;
    }
}
